home *** CD-ROM | disk | FTP | other *** search
- TITLE ASPRN - Printer Macro Expander
- PAGE 60,132
- ;======================================================================
- ; ASPRN - A printer macro expansion utility. A TSR that watches INT 17
- ; and looks for an escape sequence of the form [char]x where x={A-Z} and
- ; expands it to a setup string. Clones itself to include changes in
- ; the COM file. Different macro characters act like different programs.
- ;
- ; Usage: ASPRN [ /U | /S ][ /Mx | /Cnnn ]
- ;
- ; 9/12/87 .001 Complete memory management routines. (469)
- ; .002 Add EXPAND. (674)
- ; 10/9/87 .003 Clean-up. Start Editor (1030)
- ; .004 Restructure all code (1026)
- ; .005 Structure EDITOR proc (1200)
- ; .006 Add display and pointer routines
- ; 10/10/87 .007 String edit functions/key reading
- ; 10/11/87 .008 Extract right/left cursor (1766)
- ; 11/6/87 .009 Add HOME/END. Fix TTY. (2109)
- ; 11/7/87 .010 Cleanup. Enhance error messages (2197)
- ; 11/18/87 .011 Add labels, adjust columns. (2232)
- ; 11/22/87 .012 Add panic switch, change name (2315)
- ;----------------------------------------------------------------------
-
- CSEG SEGMENT PARA PUBLIC 'CODE'
- ASSUME CS:CSEG,DS:NOTHING,ES:NOTHING,SS:NOTHING
-
- ;----------------------------------------------------------------------
- ; Some common equates.
- ;----------------------------------------------------------------------
-
- CR EQU 13 ;Common equates
- LF EQU 10
-
- INS EQU 52H ;Extended ASCII values
- DEL EQU 53H
- F7KEY EQU 41H
- HOME EQU 47H
- ENDKEY EQU 4FH
- PGUP EQU 49H
- PGDN EQU 51H
- RARROW EQU 4DH
- LARROW EQU 4BH
- UARROW EQU 48H
- DARROW EQU 50H
- BS EQU 0E08H ;Scan/Ascii code
-
- U_SW EQU 1 ;Request to Uninstall
- S_SW EQU 2 ;Do a Setup
- M_SW EQU 4 ;Change Escape char
- ERR_SW EQU 0FFH ;General error
-
- ;----------------------------------------------------------------------
- ; Start of code.
- ;----------------------------------------------------------------------
- ORG 100H ;Starting offset for COM
- ENTPT: JMP INITIALIZE ;Skip over resident code
-
- ESC_CHAR DB "~" ;Escape char used as part
- ; of program signature
-
- COPYRIGHT DB "ASPRN 1.0 (c) 1988 Ziff Communications Co.",CR,LF
- DB "PC Magazine ",254," Robert L. Hummel",CR,LF,"$",26
-
- OLD_INT17 DW 0,0 ;Store old vector here
-
- ;----------------------------------------------------------------------
- ; A Far Pointer is kept to point to strings so they can be found anywhere
- ; in memory. The length of strings is here as well. These are updated
- ; when a modified copy is written to disk.
- ;----------------------------------------------------------------------
-
- STRING_LOC DW OFFSET STRINGS,0
- STRING_LEN DW OFFSET STRING_END - OFFSET STRINGS
-
- ;=======================================================================
- ; MACRO EXPANDER - This portion stays resident and is loaded only once.
- ; Each time a character is output to the printer, check to see if it is
- ; our escape char. If so, don't send it. The next char will indicate
- ; which string to send in it's place. If two escape chars are sent in
- ; a row, print one copy of the escape char.
- ;----------------------------------------------------------------------
- ESC_FLAG DB 0 ;=1 if last was Esc-char
- PANIC_FLAG DB 0 ;Non-zero in emergencies
-
- INT17 PROC FAR
- ASSUME CS:CSEG,DS:NOTHING,ES:NOTHING,SS:NOTHING
-
- CMP CS:PANIC_FLAG,0 ;If panic flag
- JNE INT17_0 ; do nothing
-
- STI ;Allow interrupts
- OR AH,AH ;If print char function
- JZ INT17_1 ; check for ESC_CHAR
- INT17_0:
- CLI ;Disable interrupts
- JMP DWORD PTR CS:OLD_INT17 ;Else, continue
- INT17_1:
- CMP CS:ESC_FLAG,0 ;Was last char esc?
- JNE INT17_2 ;Yes, go expand
-
- CMP AL,CS:ESC_CHAR ;Is this an esc_char
- JNE INT17_0 ;No, just print it
-
- INC CS:ESC_FLAG ; else, set latch
- INT17_1A:
- MOV AH,2 ;Get status instead
- JMP INT17_0 ; from original interrupt
- INT17_2:
- MOV CS:ESC_FLAG,0 ;Clear latch
- CMP AL,CS:ESC_CHAR ;If second esc_char
- JE INT17_0 ; output single char
-
- OR AL,20H ;Make lower case
- SUB AL,"a" ;Convert to 0-25
- CMP AL,25 ;Must be in range
- JA INT17_1A ; else ignore
-
- CALL EXPAND ;Exand the macro
- JMP INT17_1A ;Exit with status call
-
- INT17 ENDP
-
-
- ;======================================================================
- ; Expand the macro for this string combination. Enter with AL = string
- ; number (must be 0-25). DX = printer number (from original int).
- ;----------------------------------------------------------------------
- EXPAND PROC NEAR
- ASSUME CS:CSEG,DS:NOTHING,ES:NOTHING,SS:NOTHING
-
- PUSH DS ;Saved used registers
- PUSH SI
- PUSH CX
-
- LDS SI,DWORD PTR STRING_LOC ;Point DS:SI to strings
- MOV CL,AL ;Each macro occupies two
- ADD CL,CL ; strings, so double
- INC CL ; and add one
- EXP_0:
- OR CL,CL ;If at selected string
- JZ EXP_2 ; copy it to printer
- DEC CL ;Adjust counter
- EXP_1:
- LODSB ;Find the string end
- OR AL,AL ;If not terminating zero
- JNZ EXP_1 ; continue to read string
- JMP EXP_0 ;Else find next string
- EXP_2:
- LODSB ;Get a char
- OR AL,AL ;At end of string?
- JZ EXP_3 ;Yes, exit
- PUSHF ;No, simulate INT 17h
- XOR AH,AH ; print the character
- CALL DWORD PTR CS:OLD_INT17 ; through old int
- CMP AH,10h ;Check return status
- JE EXP_2 ; continue if no error
- EXP_3:
- POP CX ;Restore used registers
- POP SI
- POP DS
- RET ;And return
-
- EXPAND ENDP
-
- ;----------------------------------------------------------------------
- ; This is the smallest amount of the program that can remain in memory
- ; and still function.
- ;----------------------------------------------------------------------
- MINIMUM EQU $
-
-
- ;======================================================================
- ; SHRIVEL is really the part of INITIALIZE that makes sure we use as
- ; little memory as possible by relocating the strings downward.
- ;----------------------------------------------------------------------
- SHRIVEL PROC NEAR
- ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:NOTHING
-
- CLD ;String moves forward
- MOV SI,OFFSET STRINGS ;Source of strings
- MOV DI,OFFSET CUTOFF ;Destination
- MOV CX,STRING_LEN ;Number bytes to move
-
- MOV STRING_LOC[2],ES ;New segment of strings
- MOV STRING_LOC[0],DI ;New offset
-
- MOV DX,CX ;Save length of strings
- ADD DX,DI ; add to program length
-
- REP MOVSB ;Move DS:SI to ES:DI
-
- ADD DX,15 ;Round to nearest paragraph
- MOV CL,4 ;Convert to paras
- SHR DX,CL ; by dividing
- MOV AX,3100H ;Terminate & stay resident
- INT 21H ; thru DOS
-
- SHRIVEL ENDP
-
- ;----------------------------------------------------------------------
- ; When terminating for the first time, the SHRIVEL proc must be left
- ; resident to do the dirty work. Everything after CUTOFF is discarded
- ; or written over by the strings.
- ;----------------------------------------------------------------------
- CUTOFF EQU $
-
-
- ;======================================================================
- ; The INITIALIZE procedure performs most of the work. It interprets
- ; the command line switches, checks for previous copies, and does all
- ; the memory management. Entry is via JMP.
- ;----------------------------------------------------------------------
- NOT_RES$ DB "Not Resident$"
- CANT_GO$ DB "Cannot Uninstall$"
- SYNTAX$ DB "Usage: ASPRN [ /U | /S ][ /Mx | /Cnnn ]$"
-
- INITIALIZE PROC NEAR
- ASSUME CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG
-
- ;----------------------------------------------------------------------
- ; Set up a local stack at the end of the program.
- ;----------------------------------------------------------------------
- CLI ;Disable interrupts
- MOV SP,OFFSET STACK_TOP ;SS:SP points to stack
- STI ;Allow interrupts
-
- ;----------------------------------------------------------------------
- ; Display the copyright notice.
- ;----------------------------------------------------------------------
- MOV DX,OFFSET COPYRIGHT ;Say who we are
- MOV AH,9 ;Display string function
- INT 21H ; Thru DOS
-
- ;----------------------------------------------------------------------
- ; Release the copy of the environment allocated to this program. The
- ; segment address of the env block is located at offset 2Ch in the PSP.
- ;----------------------------------------------------------------------
- PUSH ES ;Save register
-
- MOV BX,WORD PTR DS:[2CH] ;Get environment segment
- MOV ES,BX ; in ES
- ASSUME ES:NOTHING
- MOV AH,49H ;Free allocated memory
- INT 21H ; Thru DOS
-
- POP ES ;Restore register
- ASSUME ES:CSEG
-
- ;----------------------------------------------------------------------
- ; The CMD_LINE procedure looks for switches on the command tail and
- ; returns them bit-packed in AH.
- ;
- ; /S (Setup) will invoke the editor and allow changes to be made to the
- ; loaded copy and written out to disk. No changes are made to the
- ; resident copy if one exists.
- ; /Mx (Macro char) will substitute the char x for the default escape
- ; char. If the /S parameter is specified, the char will be changed
- ; in the loaded copy and written back to disk. Otherwise, the char
- ; is used to determine residency and operation characteristics.
- ; /Cnnn (escape Char) Same as /E, but allows any char from 001-255 to
- ; be specified as a decimal number.
- ; /U (Uninstall) Will flush the resident copy if possible.
- ; Otherwise, no action. Other switches are ignored.
- ;----------------------------------------------------------------------
- CALL CMD_LINE ;Get switches in AH
- JNC INIT_1 ;If no carry, no error
-
- MOV DX,OFFSET SYNTAX$ ;Show correct syntax
- INIT_0:
- MOV AH,9 ;Display string fn
- INT 21H ; Thru DOS
-
- MOV AX,4C01h ;Terminate with error
- INT 21H ; Thru DOS
- INIT_1:
- ;----------------------------------------------------------------------
- ; Process the flags.
- ;----------------------------------------------------------------------
- TEST AH,U_SW ;Request to unload?
- JZ INIT_2 ;No, check next switch
-
- CALL UNLOAD ;Unload if possible
- JC INIT_0 ;Carry set if error
-
- MOV AX,4C00H ;Terminate okay
- INT 21H ; Thru DOS
- INIT_2:
- TEST AH,S_SW ;Setup switch on?
- JZ INIT_3 ;No, check next option
- JMP SETUP ;Yes, invoke the editor
- INIT_3:
- CALL FIND_RES ;Look for resident copy
- ASSUME ES:NOTHING ; ES may have changed
-
- JC LOAD ;No copy found - try load
- JMP REPLACE ; else, replace
-
- INITIALIZE ENDP
-
-
- ;======================================================================
- ; This procedure will cause the load copy of the program to become
- ; resident. The new copy will try to locate the strings as low in
- ; memory as possible. Hook the interrupt vectors, load the strings,
- ; and TSR. Entry via JMP.
- ;----------------------------------------------------------------------
- LOAD PROC NEAR
- ASSUME CS:CSEG, DS:CSEG, ES:CSEG
-
- PUSH ES ;Preserve register
- ASSUME ES:NOTHING ; Changed by next call
-
- MOV AX,3517H ;Get BIOS printer INT
- INT 21H ;Result in ES:BX
-
- MOV OLD_INT17[0],BX ;Save old vector
- MOV OLD_INT17[2],ES ; in local storage
-
- POP ES ;Restore register
- ASSUME ES:CSEG
-
- MOV AX,2517H ;Set new interrupt
- MOV DX,OFFSET INT17 ; to us at DS:DX
- INT 21H ; Thru DOS
-
- ;----------------------------------------------------------------------
- ; As loaded, ASPRN owns all memory from its PSP to the end of memory.
- ; Thus the only memory an allocation call will find will be below us.
- ; Try to find a low memory block to contain the strings. If success,
- ; AX contains segment of allocated block. If no room is found, discard
- ; excess code and relocate strings downward.
- ;----------------------------------------------------------------------
- CALL FIND_LOW ;Look for lower block
- JNC LOAD_1 ;No Carry if found
-
- JMP SHRIVEL ;No room in low mem
-
- ;----------------------------------------------------------------------
- ; A chunk of memory of suitable size was found below this program at
- ; segment in AX. Relocate the strings to the new area. DS:SI to AX:DI
- ; Then TSR,leaving only the macro expander resident in this segment.
- ;----------------------------------------------------------------------
- LOAD_1:
- CALL MOVE_STRINGS ;Move the strings
-
- MOV DX,(OFFSET MINIMUM - OFFSET CSEG + 15) SHR 4
- MOV AX,3100H ;Keep process resident
- INT 21H ; Thru DOS
-
- LOAD ENDP
-
-
- ;======================================================================
- ; Read the command line. Set flags in AH to indicate which switches
- ; were included. Note: the /M /C flags are eaten internally and are
- ; set for ease of later modification.
- ; 0 = none, 1 = /U, 2 = /S, 4 = /M or /C and AL = x, and FF = error.
- ; Return carry clear if valid, set if invalid.
- ; Changes: AX,BX,CX,DX,SI,DI
- ;----------------------------------------------------------------------
- WHITE DB " ,;",9 ;Space,comma,semi,tab
- WHITE_LEN EQU $ - OFFSET WHITE
-
- CMD_LINE PROC NEAR
- ASSUME CS:CSEG, DS:CSEG, ES:CSEG
-
- XOR DH,DH ;Accumulate flags here
- MOV SI,80H ;Length of cmd line
- LODSB ;Get byte in AL
- OR AL,AL ;If parameters
- JNZ CMD_1 ; process them
- CMD_0:
- CLC ;Clear carry = no error
- MOV AH,DH ;Load flags
- RET ;Return
- CMD_1:
- ;----------------------------------------------------------------------
- ; Something is on the line. Let's find out what.
- ;----------------------------------------------------------------------
- MOV BL,AL ;Put char count in BL
- CMD_2:
- OR BL,BL ;Any more chars?
- JZ CMD_0 ;If not, exit
-
- LODSB ;Get character in AL
- DEC BL ;Adjust count
- MOV DI,OFFSET WHITE ;Compare to these
- MOV CX,WHITE_LEN ;Number to check
- REPNE SCASB
- JE CMD_2 ;Jump if char was white
-
- CMP AL,"/" ;Is the char a slash?
- JE CMD_4 ;Yes, process switch
- CMD_ERR:
- MOV AH,ERR_SW ;Signal error
- STC ;Carry on
- RET ;Return
-
- ;----------------------------------------------------------------------
- ; char was slash. Get and process switch in an inelegant fashion.
- ;----------------------------------------------------------------------
- CMD_4:
- OR BL,BL ;Switch must follow /
- JZ CMD_ERR ;If not, error
-
- LODSB ;Get switch
- DEC BL ;Reduce count
- AND AL,NOT 20H ;Make switch upper case
-
- CMP AL,"U" ;Request to uninstall
- JE CMD_5 ; jump if /U
-
- CMP AL,"S" ;Setup
- JE CMD_6 ; jump if /S
-
- CMP AL,"M" ;Escape char
- JE CMD_7 ; jump if /M
-
- CMP AL,"C" ;escape Char
- JE CMD_9 ; jump if /C
-
- JMP CMD_ERR ;No more legal options
- CMD_5:
- OR DH,U_SW ;Set bit
- JMP CMD_2 ;Continue scan
- CMD_6:
- OR DH,S_SW ;Set bit
- JMP CMD_2 ;Continue scan
- CMD_7:
- OR BL,BL ;Does a char follow?
- JZ CMD_ERR ;No, syntax error
-
- LODSB ;Get new escape char
- DEC BL ;Reduce parm count
- CMD_8:
- MOV ESC_CHAR,AL ;Save in load copy
- JMP CMD_2 ;Continue scan
- CMD_9:
- MOV CX,3 ;Number of digits to read
- MOV BH,10 ;Constant to multiply by
- XOR AX,AX ;Set AX=0
- MOV DI,AX ; DI=0
- CMD_10:
- OR BL,BL ;Does a char follow?
- JZ CMD_ERR ;No, syntax error
-
- LODSB ;Get a digit in AL
- DEC BL ;Adjust count
-
- SUB AL,"0" ;Make into a number
- CMP AL,9 ;Make sure it's valid
- JA CMD_ERR ; else error
-
- XCHG DI,AX ;Get total in AX
- MUL BH ;Multiply by 10
- ADD DI,AX ;Add new digit
- LOOP CMD_10
-
- MOV AX,DI ;Get char in AL
- JMP CMD_8 ; and save it
-
- CMD_LINE ENDP
-
-
- ;======================================================================
- ; The UNLOAD proc will look for a copy of ASPRN in memory that has the
- ; same macro character, and unload if found. Carry clear if successful.
- ; Set if error and DX points to error msg to display.
- ;----------------------------------------------------------------------
- UNLOAD PROC NEAR
- ASSUME CS:CSEG, DS:CSEG, ES:CSEG
-
- ;----------------------------------------------------------------------
- ; Check if already loaded in memory. Don't load multiple copies.
- ; When search terminates
- ; ES = BX = segment of first matching copy found in memory.
- ; CS = DS = AX = segment of current copy as loaded from disk.
- ; If no previous copy found
- ; CS = ES = AX = BX
- ; If previous copy IS found
- ; (CS = DS = AX) != (ES = BX)
- ;----------------------------------------------------------------------
- CALL FIND_RES ;Look for resident copy
- ASSUME ES:NOTHING ;May be changed by proc
-
- MOV DX,OFFSET NOT_RES$ ;Default error msg
- JNC UNLOAD_1 ;NC if successful, jump
- RET ;Return with carry set
-
- ;----------------------------------------------------------------------
- ; A previous copy was found in memory. Now CS=DS=new copy. ES=BX=old
- ; copy. Get the segment for the current printer interrupt. If it's
- ; the same as the seg of the resident copy, then we can deinstall.
- ;----------------------------------------------------------------------
- UNLOAD_1:
- PUSH BX ;Save resident segment
-
- MOV AX,3517H ;Get current BIOS prn INT
- INT 21H ;Result in ES:BX
-
- POP BX ;Discard offset, retrieve
- ; resident segment
-
- MOV DX,OFFSET CANT_GO$ ;Default error message
-
- MOV AX,ES ;Int 17 segment in AX (ES)
- CMP AX,BX ; same as resident segment?
-
- JE UNLOAD_2 ;Yes, remove
-
- ;----------------------------------------------------------------------
- ; Another program has intercepted the printer and we cannot deinstall.
- ; Turn on the panic switch so that we'll be meerly disabled.
- ;----------------------------------------------------------------------
- UNLOAD_1A:
- PUSH BX ;Point to resident
- POP ES ; segment again
- MOV ES:PANIC_FLAG,0FFH ;Disable expander
- STC ;Indicate error
- RET ;Return
-
- ;----------------------------------------------------------------------
- ; No other TSRs are loaded after us. Perform the removal.
- ; 1. Restore Int 17h to its previous value.
- ; 2. Release the MCB that we allocated to hold the strings.
- ; 3. If code seg different than string block, release MCB for program.
- ; Note that ES points to the resident segment.
- ;----------------------------------------------------------------------
- UNLOAD_2:
- LDS DX,DWORD PTR ES:OLD_INT17 ;Get saved vector
- ASSUME DS:NOTHING ;Changes DS
- MOV AX,2517H ;Restore it
- INT 21H ; Thru DOS
-
- LES AX,DWORD PTR ES:STRING_LOC ;String segment in ES
- MOV AH,49H ;Free strings block
- INT 21H ;Release seg in ES
- JC UNLOAD_1A ;Panic if error
-
- MOV AX,ES ;Move seg to AX
- CMP AX,BX ;If = program segment
- JE UNLOAD_3 ; return
-
- MOV ES,BX ;Free res code
- MOV AH,49H ;Free strings
- INT 21H ; Thru DOS
- JC UNLOAD_1A ;Panic if error
- UNLOAD_3:
- CLC ;Clear carry flag
-
- PUSH CS ;Reset DS and ES
- POP DS ; back to CS
- PUSH CS
- POP ES
- RET
-
- UNLOAD ENDP
-
-
- ;======================================================================
- ; Search memory to see if a copy of this program has already been
- ; loaded by looking for copyright notice.
- ; If found, NC, CS = DS = AX = LOAD COPY, ES = BX = RES COPY
- ; If not CY, CS = DS = AX = ES = BX = LOAD COPY
- ;----------------------------------------------------------------------
- FIND_RES PROC NEAR
- ASSUME CS:CSEG, DS:CSEG, ES:NOTHING
-
- CLD ;String moves forward
- NOT WORD PTR [ENTPT] ;Modify to avoid false match
-
- MOV BX,600H ;BX = segment to compare
- MOV AX,CS ;AX = our segment
- FIND_RES_1:
- INC BX ;Next paragraph
- MOV ES,BX ;Set ES to search segment
- CMP AX,BX ;If not load copy
- JNE FIND_RES_2 ; test for copyright
- STC ;Else, flag failure
- RET
- FIND_RES_2:
- MOV SI,OFFSET ENTPT ;String to compare
- MOV DI,SI ;Offset is same
- MOV CX,16 ;Compare first 16 bytes
- REP CMPSB ;CMP DS:SI TO ES:DI
- OR CX,CX ;All matched?
- JNZ FIND_RES_1 ;No, continue search
-
- CLC ;Set NC = success
- RET
-
- FIND_RES ENDP
-
-
- ;======================================================================
- ; The program has been loaded normally, and is already resident.
- ; Just replace the old strings with the new strings.
- ; ES = BX = TSR segment
- ;----------------------------------------------------------------------
- BADREPLACE$ DB CR,LF,"ASPRN Failed. Suggest Reboot.",CR,LF,"$"
- NEWSEGLEN DW 0
-
- REPLACE PROC NEAR
- ASSUME CS:CSEG, DS:CSEG, ES:NOTHING
-
- ;----------------------------------------------------------------------
- ; If the old string segment (as recorded in old copy) is the same as
- ; the resident program segment, then they must be chopped off.
- ;----------------------------------------------------------------------
- MOV AH,50H ;Set active PSP
- MOV BX,ES ; to TSR
- INT 21H ;Undocumented DOS
-
- MOV AX,ES:STRING_LOC[2] ;Is old string segment
- CMP AX,BX ; same as old prog seg?
- JE REPLACE_1 ;Yes, cut them off.
-
- ;----------------------------------------------------------------------
- ; The strings are in a separate block, so we can just release them.
- ;----------------------------------------------------------------------
- PUSH ES ;Save ES
-
- MOV ES,AX ;Segment to release
- MOV AH,49H ;Free allocated memory
- INT 21H ; Thru DOS
-
- POP ES ;Restore segment
-
- JNC REPLACE_2 ;Continue if no erro
- JMP REPLACE_4
-
- ;----------------------------------------------------------------------
- ; Strings are still attaced to the old ASPRN.COM file. We must surgically
- ; remove them. Shrink the old ES block down to hold just the program.
- ;----------------------------------------------------------------------
- REPLACE_1:
- MOV BX,(OFFSET MINIMUM - OFFSET CSEG + 15) SHR 4
- MOV AH,4AH ;Setblock
- INT 21H ;Shrink ES
- JC REPLACE_4
-
- ;----------------------------------------------------------------------
- ; Try to locate a block large enough to contain the strings below
- ; current program (in lower memory).
- ;----------------------------------------------------------------------
- REPLACE_2:
- CALL FIND_LOW ;Look for memory block
- JC REPLACE_3 ;Jump if not found
-
- ;----------------------------------------------------------------------
- ; Room was found. The new segment was returned in AX.
- ; Relocate the strings. Update the pointers in the resident copy.
- ;----------------------------------------------------------------------
- CALL MOVE_STRINGS ;Copy strings to block
-
- MOV AH,50H ;Set active PSP
- MOV BX,CS ; to US
- INT 21H ; Thru DOS
- REPLACE_2A:
- MOV AX,4C00H ;All done! Terminate.
- INT 21H ; Thru DOS
-
- ;======================================================================
- ; There is no room in low memory, so we want to relocate our strings
- ; to the lowest possible address. We currently own all high memory.
- ; Shrink this copy's memory down to just enough to hold the program,
- ; strings, and STACK.
- ;----------------------------------------------------------------------
- REPLACE_3:
- MOV AH,50H ;Set active PSP
- MOV BX,CS ; back to us
- INT 21H ; Undocumented DOS
-
- PUSH ES ;Save register
-
- MOV AH,4AH ;Change size of blcok
- MOV BX,(OFFSET LASTBYTE - OFFSET CSEG + 15) SHR 4
- MOV NEWSEGLEN,BX ;Save this size
-
- PUSH CS ;Set ES to CS
- POP ES ; i.e., segment to modify
- INT 21H ; Shrink thru DOS
-
- POP ES ;Retrive TSR segment
- JNC REPLACE_5
- REPLACE_4:
- MOV DX,OFFSET BADREPLACE$ ;Indicate an error
- MOV AH,9 ;Display string
- INT 21H ; Thru DOS
-
- MOV ES:PANIC_FLAG,0FFH ;Disable expander
-
- MOV AX,4CFFH ;Terminate with error
- INT 21H ; Thru DOS
-
-
-
- ;----------------------------------------------------------------------
- ; Ask for 640K. We'll get an error and BX will contain the largest
- ; piece of memory available. One of the better DOS functions returns.
- ; New block will be above us. Allocate all of it.
- ;----------------------------------------------------------------------
- REPLACE_5:
- MOV AH,48H ;Allocate memory
- MOV BX,0FFFFH ;Ask for 640K
- INT 21H ;Available returned in BX
-
- CMP BX,(OFFSET LASTBYTE - OFFSET CSEG + 15) SHR 4
- JB REPLACE_4
-
- MOV AH,48H ;Allocate BX (all) paras
- INT 21H ;AX = new segment
- JC REPLACE_4
-
- ;----------------------------------------------------------------------
- ; Duplicate the program at the new address. Copy from ds:si to es:di
- ;----------------------------------------------------------------------
- XOR SI,SI ;SI = 0
- MOV DI,SI ;DI = 0
- PUSH ES ;Save TSR segment
-
- MOV ES,AX ;New block
- MOV CX,OFFSET LASTBYTE ;Bytes to move
- CLD
- REP MOVSB ;Copy to new address
-
- POP ES
-
- ;----------------------------------------------------------------------
- ; Now, hop up to our new home by using a far return.
- ;----------------------------------------------------------------------
- PUSH AX ;Put new CS on stack
- MOV DX,OFFSET TARGET ;And address of the
- PUSH DX ; next instruction
- CLI ;Turn off interrupts
-
- DB 0CBH ;Opcode for RETF
-
- ;----------------------------------------------------------------------
- ; Now we're at AX:TARGET, in the copy of the program. Make it real.
- ;----------------------------------------------------------------------
- TARGET:
- PUSH CS ;Move stack - lose old
- POP SS ;Change segment
- MOV SP,OFFSET STACK_TOP ; and offset
- STI ;Allow interrrupts
-
- MOV BX,AX ;Get the new PSP
- MOV AH,50H ;Set active PSP
- INT 21H ; Undocumented DOS
-
- ;----------------------------------------------------------------------
- ; Release the memory held by the old copy of the program at DS.
- ;----------------------------------------------------------------------
- PUSH ES ;Save register
-
- PUSH DS ;Put loaded PSP seg
- POP ES ; in ES
-
- MOV AH,49H ;Free memory
- INT 21H ; Thru DOS
-
- POP ES ;Restore register
- JC REPLACE_4 ;Panic if error
-
- PUSH CS ;Set DS to this new seg
- POP DS
-
- ;----------------------------------------------------------------------
- ; Now find the a block for the strings.
- ;----------------------------------------------------------------------
- BREAKPT:
- MOV AH,50H ;Block must belong to
- MOV BX,ES ; resident copy
- INT 21H
-
- CALL FIND_LOW
-
- ;----------------------------------------------------------------------
- ; Copy strings from the new copy at CS:STRINGS newly allocated block.
- ; Point ES to the RES copy to update the parameters.
- ;----------------------------------------------------------------------
- CALL MOVE_STRINGS ;Transfer strings
-
- ;----------------------------------------------------------------------
- ; Terminate through bogus PSP.
- ;----------------------------------------------------------------------
- MOV AH,50H ;Set active PSP
- MOV BX,CS ; to right here
- INT 21H ;Undocumented DOS again
-
- PUSH CS ;Point ES to the
- POP ES ; current segment
-
- MOV AH,4AH ;Setblock
- MOV BX,NEWSEGLEN ;Same size
- INT 21H ; Thru DOS
- JC REPLACE_4 ;Panic if error
-
- MOV AX,4C00H ;Terminate thru new PSP
- INT 21H ; Thru DOS
-
- REPLACE ENDP
-
-
- ;======================================================================
- ; Look for a piece of memory large enough to hold DS:STRING_LEN bytes.
- ; This is always taken from the newest program being loaded.
- ; Changes: AX,BX,CL
- ;----------------------------------------------------------------------
- FIND_LOW PROC NEAR
- ASSUME CS:CSEG, DS:CSEG, ES:NOTHING
-
- MOV AH,48H ;Allocate memory
- MOV BX,STRING_LEN ;Change length in bytes
- ADD BX,15
- MOV CL,4
- SHR BX,CL ; to paras
- INT 21H ; Thru DOS
-
- RET
-
- FIND_LOW ENDP
-
-
- ;======================================================================
- ; Relocate the strings from DS:STRINGS TO AX:0. Update the pointer
- ; and length in the resident copy.
- ;----------------------------------------------------------------------
- MOVE_STRINGS PROC NEAR
- ASSUME CS:CSEG, DS:CSEG, ES:NOTHING
-
- PUSH ES ;Save resident segment
-
- MOV CX,STRING_LEN ;Bytes to move
- MOV ES:STRING_LEN,CX ;Update resident copy
-
- XOR DI,DI ;Copy to offset 0
- MOV ES:STRING_LOC[0],DI ;New offset
-
- MOV ES:STRING_LOC[2],AX ;New segment
- MOV ES,AX ;Destination is ES:DI
-
- MOV SI,OFFSET STRINGS ;Source is DS:SI
- CLD ;String moves forward
- REP MOVSB ; WHAM!
-
- POP ES ;Restore segment
- RET
-
- MOVE_STRINGS ENDP
-
-
- ;======================================================================
- ; This proc allows you to edit the strings. On entry CS=DS=ES.
- ; Total of program code + strings cannot exceed 64K, which is the
- ; maximum size of a COM file.
- ;----------------------------------------------------------------------
- MEMORY$ DB "Not Enough Memory$"
- SAVE$ DB CR,LF,"Save changes as ASPRNNEW.COM? (Y/N) $"
- OVERWRITE$ DB CR,LF,"Overwrite existing file? (Y/N) $"
- FERROR$ DB CR,LF,"File error. Try Again? (Y/N) $"
- WERROR$ DB CR,LF,"Write error. Try Again? (Y/N) $"
-
- FILENAME DB "ASPRNNEW.COM",0
- COM_PTR DW OFFSET STRING_END ;Cannot exceed 64k-200H
-
- ROW_END DB 24 ;Defaults for
- COL_END DB 80 ; common video
- COL_MAX DB 0 ;Rightmost column
- VPAGE DB 0 ;Active page
-
- ATTR DB 0 ;Selected attribute
-
- ;----------------------------------------------------------------------
- SETUP PROC NEAR
- ASSUME CS:CSEG, DS:CSEG, ES:CSEG
-
- MOV AH,4AH ;Modify memory block
- MOV BX,1000H ;Ask for 64K
- INT 21H ; Thru DOS
- JNC SETUP_0 ;Jump if no error
-
- MOV DX,OFFSET MEMORY$ ;Need more room
- MOV AH,9 ;Display string
- INT 21H ; Thru DOS
-
- MOV AX,4C02H ;Terminate with error
- INT 21H ; Thru DOS
- SETUP_0:
- CLI ;Disable interrupts
- MOV SP,0FFFEH ;Move stack to end of seg
- STI ;Enable interrupts
-
- ;----------------------------------------------------------------------
- ; Editing requires that we be in a text mode.
- ; Clear entire screen to desired attribute.
- ;----------------------------------------------------------------------
- CALL VIDEO_SETUP ;Examine video hardware
-
- MOV AL,COL_END ;Right edge of screen
- SUB AL,2 ;(1 based) in one char
- MOV COL_MAX,AL ;Is rightmost column
-
- CALL CLR_BOX ;Draw the window
-
- ;----------------------------------------------------------------------
- ; Invoke the string editor. Returns when F7 is pressed.
- ;----------------------------------------------------------------------
- CALL EDIT ;String editor
-
- MOV CX,COM_PTR ;Get program length
- SUB CX,OFFSET STRINGS ; minus start of strings
- MOV STRING_LEN,CX ; is string length
-
- ;----------------------------------------------------------------------
- ; Ask if changes should be written out to ASPRNNEW.COM. If not, just end.
- ;----------------------------------------------------------------------
- SETUP_1:
- MOV DX,OFFSET SAVE$ ;Clone the changes?
- CALL GETRESPONSE ;Yes or No.
- JC SETUP_2 ;Yes, continue
-
- MOV AX,4C03H ;Terminate normally
- INT 21H ; Thru DOS
-
- ;----------------------------------------------------------------------
- ; Try to open the file to see if it exists.
- ;----------------------------------------------------------------------
- SETUP_2:
- MOV AX,3D02H ;Open file for r/w
- MOV DX,OFFSET FILENAME ; This name
- INT 21H ; Thru DOS
- JC SETUP_3 ;Jump if not found
-
- MOV BX,AX ;Move handle
-
- MOV DX,OFFSET OVERWRITE$ ;Should we overwrite?
- CALL GETRESPONSE
- JC SETUP_4 ;Yes
- SETUP_2A:
- MOV AH,3EH ;Close file handle
- INT 21H ; Thru DOS
- JMP SETUP_1 ;Ask again
-
- ;----------------------------------------------------------------------
- ; File does not exist. Attempt to open as new.
- ;----------------------------------------------------------------------
- SETUP_3:
- MOV AH,3CH ;Create file fn
- XOR CX,CX ; for writing
- MOV DX,OFFSET FILENAME ; this is name
- INT 21H ; Thru DOS
- JNC SETUP_3A ;Opened OK, jump
-
- MOV DX,OFFSET FERROR$ ;Error opening file
- CALL GETRESPONSE ; try again?
- JC SETUP_3 ;Yes
- JMP SETUP_1 ;No
- SETUP_3A:
- MOV BX,AX ;Put handle in BX
-
- ;----------------------------------------------------------------------
- ; A valid file handle is in BX. Write away.
- ;----------------------------------------------------------------------
- SETUP_4:
- MOV AH,40H ; Write to file fn
- MOV CX,COM_PTR ; Length
- SUB CX,100H ; Minus PSP length
- MOV DX,100H ; Pointer to DTA
- INT 21H ; Thru DOS
- JC SETUP_5 ;CY signals error
-
- CMP AX,CX ;All bytes written?
- JE SETUP_6 ;Yes
-
- ;----------------------------------------------------------------------
- ; An error was encountered on the write.
- ;----------------------------------------------------------------------
- SETUP_5:
- MOV DX,OFFSET WERROR$
- CALL GETRESPONSE ;Try again?
- JC SETUP_4 ;Yes
- JMP SETUP_2A ;No
-
- ;----------------------------------------------------------------------
- ; File was written okay. Close and exit.
- ;----------------------------------------------------------------------
- SETUP_6:
- MOV AH,3EH ;Close file handle in BX
- INT 21H ; Thru DOS
-
- MOV AX,4C00H ;Terminate
- INT 21H ; Thru DOS
-
- SETUP ENDP
-
-
- ;======================================================================
- ; Accept only a Y or N answer. Return CY if YES, NC if NO. DX contains
- ; offset of prompt to print.
- ;----------------------------------------------------------------------
- GETRESPONSE PROC NEAR
- ASSUME CS:CSEG, DS:CSEG, ES:CSEG
-
- MOV AH,9 ;Display string fn
- INT 21H ; Thru DOS
- GETR_0:
- CALL GETKEY ;Get a keystroke
-
- AND AL,NOT 20H ;Capitalize
-
- CMP AL,"N" ;If NO,
- JNE GETR_1
- ;If equal, CY is off
- RET ; just end
- GETR_1:
- CMP AL,"Y" ;If not YES,
- JNE GETR_0 ; try again
- STC ;Carry on
- RET
-
- GETRESPONSE ENDP
-
-
- ;======================================================================
- ; Determine all the paramters and info we need to handle the display.
- ; Return with carry set if incomaptible mode.
- ;----------------------------------------------------------------------
- COLOR_ATTR EQU 1FH ;Brite white/blue
- BW_ATTR EQU 07H ;Reverse video
-
- VIDEO_SETUP PROC NEAR
- ASSUME CS:CSEG, DS:CSEG, ES:CSEG
-
- MOV AH,0FH ;Get video mode
- INT 10H ; Thru BIOS
-
- MOV ATTR,COLOR_ATTR ;Assume color screen
-
- CMP AL,3 ;CGA video modes
- JBE VID_3 ; are okay
-
- CMP AL,7 ;MDA text mode
- JE VID_2 ; is okay, too.
-
- STC ;Else, an error
- RET ;Return
- VID_2:
- MOV ATTR,BW_ATTR ;Force B/W
- VID_3:
- MOV COL_END,AH ;Save cols
- MOV VPAGE,BH ;Save current page
-
- ;----------------------------------------------------------------------
- ; Determine if an EGA/VGA adapter is installed, and find row count.
- ;----------------------------------------------------------------------
- MOV AH,12H ;EGA alternate select
- MOV BL,10H ;Return EGA info
- INT 10H ;Thru BIOS
- CMP BL,10H ;If BL unchanged
- MOV DL,24 ;Set default rows
- JE VID_4 ; there's no EGA/VGA
-
- PUSH ES ;Changed by call
- MOV AX,1130H ;EGA info call
- MOV BH,0 ;Dummy argument
- INT 10H ; thru BIOS
- POP ES
- VID_4:
- MOV ROW_END,DL ;Save rows
- CLC
- RET
-
- VIDEO_SETUP ENDP
-
- ;======================================================================
- ; Position the cursor to the stored values.
- ; Changes BX
- ;----------------------------------------------------------------------
- CUR_SET PROC NEAR
- ASSUME CS:CSEG, DS:CSEG, ES:NOTHING
-
- PUSH AX ;Save used register
-
- MOV AH,2 ;Position cursor fn
- MOV BH,VPAGE ; current page
- MOV DX,CURSOR_POS ; new cursor position
- INT 10H ; Thru BIOS
-
- POP AX ;Restore register
- RET
-
- CUR_SET ENDP
-
- ;======================================================================
- ; The EDIT procedure handles all the editing. It keeps track of the
- ; current macro strings and displays them on the screen as they change.
- ;----------------------------------------------------------------------
-
- ; The PTR offset points to the character that appears at the left side
- ; of the window
-
- PTR_ARRAY LABEL WORD ;Indicates the starting
- NAM_PTR DW 0 ; offset of the current
- STR_PTR DW 0 ; macro name and string
-
- ACTIVE DW 0 ;Index for array
-
- ; The screen column for positioning the cursor. Common data.
-
- CURSOR_POS LABEL WORD
- CURSOR_COL DB 6 ;Current cursor
- CURSOR_ROW DB 0 ; position
-
- MACRO_PTR DB 0 ;Pointer to string set
-
- INS_STATE DB 0 ;0=INS FF=TYPEOVER
-
- ;======================================================================
- EDIT PROC NEAR
- ASSUME CS:CSEG, DS:CSEG, ES:CSEG
-
- MOV MACRO_PTR,0 ;Choose first string
-
- ;----------------------------------------------------------------------
- ; Display the Macro letter for these strings.
- ;----------------------------------------------------------------------
- EDIT_1:
- MOV CURSOR_POS,0106H ;Move to row 1 col 2
- CALL CUR_SET
-
- MOV AH,0AH ;Write character
- MOV AL,ESC_CHAR ; char to write
- MOV CX,1 ; repeat count
- INT 10H ; Thru BIOS
-
- INC CURSOR_COL ;Move to next column
- CALL CUR_SET
-
- MOV AH,0AH ;Write character
- MOV AL,MACRO_PTR ; number of macro
- ADD AL,"A" ; convert to letter
- MOV CX,1 ; repeat count
- INT 10H ; Thru BIOS
-
- ;----------------------------------------------------------------------
- ; Initialize the pointers to point to the first macro set.
- ;----------------------------------------------------------------------
- CALL LOAD_POINTERS
-
- ;----------------------------------------------------------------------
- ; Display the selected strings on the screen as read from memory.
- ;----------------------------------------------------------------------
- MOV BX,0 ;Index for Name
-
- CALL MAKE_ACTIVE ;Put cursor in NAME
- CALL DISPLAY ;Show the string
-
- MOV BX,2 ;Index for Macro
- EDIT_2:
- CALL MAKE_ACTIVE ;Put cursor in MACRO
- CALL DISPLAY ;Show the string
-
- ;----------------------------------------------------------------------
- ; Get a key from the keyboard and act on it.
- ;----------------------------------------------------------------------
- KEY_0:
- CALL GETKEY ;Read a key into AX
-
- OR AL,AL ;If 0, is extended
- JZ KEY_2 ; which means command
-
- CMP AX,BS ;If not actual BS key
- JNE KEY_1 ;Process as char
-
- OR AH,AH ;If high byte is zero
- JZ KEY_1 ;Process as char
-
- ;----------------------------------------------------------------------
- ; The backspace key is the only key that requires special handling.
- ; Treat BS as a CURSOR-LEFT/DELETE combination.
- ;----------------------------------------------------------------------
- CALL CURSOR_LEFT
- JC KEY_0
- KEY_0A:
- CALL STRING_DEL ;Delete char at cursor
- JC KEY_0
-
- CALL DISPLAY
-
- CMP ACTIVE,0 ;If deleted from first
- JNE KEY_0
-
- DEC PTR_ARRAY[2] ;Back up second
- JMP KEY_0
-
- ;----------------------------------------------------------------------
- ; Put the character on the screen and in the string.
- ;----------------------------------------------------------------------
- KEY_1:
- CMP INS_STATE,0 ;If insert
- JE KEY_1A ; jump
-
- ;----------------------------------------------------------------------
- ; If at end of string, typeover works just like insert.
- ;----------------------------------------------------------------------
- CALL LOCATE_SI ;If current char
- CMP BYTE PTR [SI],0 ; isn't a zero byte
- JNZ KEY_1B ; just overwrite
- KEY_1A:
- CALL STRING_INS ;Create hole at cursor
- CMP ACTIVE,0 ;If inserting first
- JNE KEY_1B
-
- INC PTR_ARRAY[2] ;Advance second
- KEY_1B:
- CALL PUTCHAR ;Put AL at cursor
- CALL DISPLAY ;Show changes
- ;Fall through
- ;----------------------------------------------------------------------
- ; -> Move the cursor to the right one space.
- ;----------------------------------------------------------------------
- KEY_1C:
- CALL CURSOR_RIGHT ;Move cursor along
- JMP KEY_0
-
- ;----------------------------------------------------------------------
- ; Key is an extended key. Must be an instruction.
- ;----------------------------------------------------------------------
- KEY_2:
- CMP AH,F7KEY ;F7 is the exit key
- JNE KEY_3
-
- MOV CURSOR_COL,0 ;Reposition cursor
- MOV CURSOR_ROW,NROW ; for message
- CALL CUR_SET
-
- RET ; and the only way out
-
- ;----------------------------------------------------------------------
- ; All remaining key dispatch done from here.
- ;----------------------------------------------------------------------
- KEY_3:
- MOV BL,MACRO_PTR ;Number of set
-
- CMP AH,DEL ;Kill char at cursor
- JE KEY_0A
-
- CMP AH,PGUP ;Check for PgUp
- JE KEY_3A ; else check next
-
- CMP AH,PGDN ;Move to next macro
- JE KEY_5
-
- MOV BX,ACTIVE
-
- CMP AH,RARROW ;Move right 1 char
- JE KEY_1C
-
- CMP AH,LARROW ;Move left
- JE KEY_9
-
- CMP AH,UARROW ;Move up
- JE KEY_12
-
- CMP AH,DARROW ;Move down
- JE KEY_14
-
- CMP AH,INS ;Use Insert mode
- JE KEY_15
-
- CMP AH,ENDKEY ;Move to end of string
- JE KEY_16
-
- CMP AH,HOME ;Move to start of string
- JE KEY_17
-
- JMP KEY_0 ;Didn't recongnize it
-
- ;----------------------------------------------------------------------
- ; PgUp key: Move to the previous macro.
- ;----------------------------------------------------------------------
- KEY_3A:
- DEC BL ;Back up
- CMP BL,0 ;If below 0
- JGE KEY_4
-
- MOV BL,25 ; reset to end
- KEY_4:
- MOV MACRO_PTR,BL ;Update pointer
- JMP EDIT_1 ;Start over
-
- ;----------------------------------------------------------------------
- ; PgDn key: Move to the next macro.
- ;----------------------------------------------------------------------
- KEY_5:
- INC BL ;Go forward
- CMP BL,25 ;If past end
- JBE KEY_4
-
- XOR BL,BL ;Reset
- JMP KEY_4
-
- ;----------------------------------------------------------------------
- ; <- Move the cursor to the left one space.
- ;----------------------------------------------------------------------
- KEY_9:
- CALL CURSOR_LEFT ;Move cursor left
- KEY_10:
- JMP KEY_0 ;Failed, ignore it
-
- ;----------------------------------------------------------------------
- ; ^ Move to the NAME field.
- ;----------------------------------------------------------------------
- KEY_12:
- CMP BX,0 ;If already active
- JE KEY_10 ; ignore
-
- MOV BX,0 ;Else, switch
- JMP EDIT_2
-
- ;----------------------------------------------------------------------
- ; v Move to the STRING field.
- ;----------------------------------------------------------------------
- KEY_14:
- CMP BX,2 ;If already active
- JE KEY_10 ; ignore
-
- MOV BX,2 ;else, switch
- JMP EDIT_2
-
- ;----------------------------------------------------------------------
- ; Toggle the insert/typeover state.
- ;----------------------------------------------------------------------
- KEY_15:
- NOT INS_STATE ;Toggle the flag
- JMP KEY_0
-
- ;----------------------------------------------------------------------
- ; Move to end of string.
- ;----------------------------------------------------------------------
- KEY_16:
- CALL CURSOR_RIGHT ;Move to the right
- JNC KEY_16 ; as long as successful
- JMP KEY_0
-
- ;----------------------------------------------------------------------
- ; Move to start of string.
- ;----------------------------------------------------------------------
- KEY_17:
- CALL CURSOR_LEFT ;Move to the left
- JNC KEY_17 ; as long as successful
- JMP KEY_0
-
- EDIT ENDP
-
-
- ;======================================================================
- ; Clear a window (box) for our information on the screen.
- ; Add a border for a nice touch.
- ;----------------------------------------------------------------------
- TITLE$ DB 0B5H,"ASPRN 1.0",0C6H,0
- TITLE_LEN EQU $-TITLE$
- HELP$ DB "STRING: ",27,32,26," INS DEL ",24,32,25
- DB " MACRO: PgUp PgDn F7 = Save",0
- NAME$ DB "NAME :",0
- MACRO$ DB "MACRO:",0
-
- BOX_CHARS DB 0C9H,0CDH,0BBH,0BAH,020H,0BAH
- DB 199,196,182,0C8H,0CDH,0BCH
-
- NROW EQU 9
-
- CLR_BOX PROC NEAR
- ASSUME CS:CSEG, DS:CSEG, ES:NOTHING
-
- MOV AX,0700H ;Scroll window fn
- MOV BH,ATTR ; clear to this color
- XOR CX,CX ;Start row,col
- MOV DH,ROW_END ;End row,col
- MOV DL,COL_END
- DEC DL
- INT 10H ;Thru BIOS
-
- MOV BH,VPAGE ;Get active page
- MOV SI,OFFSET BOX_CHARS ;Draw the edit window
- MOV DX,CX ;Cursor from last call
- MOV CX,NROW ;Number of rows to draw
- CB_1:
- PUSH CX ;Save counter
- MOV DL,0
-
- MOV AH,2 ;Position cursor
- INT 10H ;Thru BIOS
-
- LODSB ;Get leftmost char
- MOV AH,0EH ;Write char TTY
- INT 10H ;Thru BIOS
-
- LODSB ;Get middle char
- MOV AH,0AH ;Write repeated char
- MOV CL,COL_END ;Width of box
- XOR CH,CH
- SUB CX,2 ; minus 2 sides
- INT 10H ;Thru BIOS
-
- MOV AH,2 ;Position cursor
- MOV DL,0
- ADD DL,COL_END
- DEC DL ;Col = righthand edge
- INT 10H ;Thru BIOS
-
- LODSB ;Get rightmost char
- MOV AH,0AH ;Write char
- MOV CX,1
- INT 10H ;Thru BIOS
-
- INC DH ;Next row
- POP CX ;Restore counter
-
- CMP CL,NROW ;Examine row we wrote
- JE CB_2 ;If first row
-
- CMP CL,2 ;or next to last
- JNE CB_1A ;Don't adjust count
- ADD SI,3
- CB_1A:
- TEST CL,1 ;If row is even
- JZ CB_2 ;Don't adjust count
- SUB SI,6
- CB_2:
- LOOP CB_1
-
- MOV CURSOR_ROW,0 ;Top row
- MOV AL,COL_MAX ;Rightmost column
- SUB AL,TITLE_LEN+5 ;Backup
- MOV CURSOR_COL,AL ; to here
-
- MOV SI,OFFSET TITLE$ ;Program name
- CALL CB_3
-
- MOV CURSOR_POS,0701H
- MOV SI,OFFSET HELP$ ;Instructions
- CALL CB_3
-
- MOV CURSOR_POS,0301H
- MOV SI,OFFSET NAME$ ;And titles
- CALL CB_3
-
- MOV CURSOR_POS,0501H
- MOV SI,OFFSET MACRO$
- CB_3:
- CALL CUR_SET ;Position cursor
- CB_3A:
- MOV BH,VPAGE ;Use active page
- LODSB ;Get a char
- OR AL,AL ;If zero
- JZ CB_4 ; quit
- MOV AH,0EH ;Else, write TTY
- INT 10H ; Thru BIOS
- JMP CB_3A ;Continue
- CB_4:
- RET
-
- CLR_BOX ENDP
-
- ;======================================================================
- ; Write the character in AL to the string as SI.
- ; Changes: SI
- ; Calls: LOCATE_SI
- ;----------------------------------------------------------------------
- PUTCHAR PROC NEAR
-
- CALL LOCATE_SI ;Point to cursor location
- MOV [SI],AL ; and pop in char
- RET
-
- PUTCHAR ENDP
-
-
- ;======================================================================
- ; Point SI to the same char in the string that is currently above the
- ; cursor on the screen.
- ; Changes: BX,SI,CX
- ;----------------------------------------------------------------------
- LOCATE_SI PROC NEAR
-
- MOV BX,ACTIVE ;Get active index
- MOV SI,PTR_ARRAY[BX] ;Read string from this pt
-
- XOR CH,CH ;Adjust the start of
- MOV CL,CURSOR_COL ;the string to point
- SUB CL,7 ;to the char at the
- ADD SI,CX ;cursor
-
- RET
-
- LOCATE_SI ENDP
-
-
- ;======================================================================
- ; Create a hole in the string by moving everything to the right.
- ; Changes: SI,DI,CX
- ; Calls: LOCATE_SI
- ;----------------------------------------------------------------------
- STRING_INS PROC NEAR
-
- CALL LOCATE_SI ;SI = current char
-
- MOV CX,COM_PTR ;End of strings offset
- MOV DI,CX ;Is also target for move
- SUB CX,SI ;Bytes to move
-
- MOV SI,DI ;Copy to source register
- DEC SI ;Copy from previous byte
-
- STD ;Move backwards
- REP MOVSB ; whole string
- INC COM_PTR ;File is longer
-
- RET
-
- STRING_INS ENDP
-
-
- ;======================================================================
- ; Delete the char at the cursor. Close up the string.
- ; Changes: CX,SI,DI
- ; Calls: LOCATE_SI
- ;----------------------------------------------------------------------
- STRING_DEL PROC NEAR
-
- CALL LOCATE_SI ;Point to current char
- CMP BYTE PTR [SI],0 ;Can't backup too far
- JNZ SD_1
- STC ;Error
- RET
- SD_1:
- MOV CX,COM_PTR ;End of strings offset
- SUB CX,SI ;Bytes to move
- DEC CX ;Is one less
-
- MOV DI,SI
- INC SI ;Copy from previous byte
-
- CLD ;Move backwards
- REP MOVSB
- DEC COM_PTR ;File gets shorter
- CLC
- RET
-
- STRING_DEL ENDP
-
-
- ;======================================================================
- ; Move the cursor left/right 1 char. Return NC if success. CY if fail.
- ; Changes: SI,CX
- ; Calls: LOCATE_SI, CUR_SET, DISPLAY
- ;----------------------------------------------------------------------
- CURSOR_RIGHT PROC NEAR
-
- CALL LOCATE_SI
- CMP BYTE PTR [SI],0 ;Are we on last char
- JNE CR_0 ;of string? jmp if yes
- CR_A:
- STC ;Signal failure
- RET
- CR_0:
- MOV CL,CURSOR_COL
- CMP CL,COL_MAX ;Is cursor at screen edge
- JE CR_2 ;yes, jump
-
- INC CL ;Move to next col
- CR_1:
- MOV CURSOR_COL,CL ;Save (getkey updates)
- CALL CUR_SET
- CLC ;Signal success
- RET
- CR_2:
- INC PTR_ARRAY[BX] ;Move the start
-
- PUSH CURSOR_POS ;Save current cursor
-
- MOV CURSOR_COL,7 ;Redisplay from left side
- CALL CUR_SET ;Set cursor
- CALL DISPLAY ;Draw string
-
- POP CURSOR_POS ;Reset old cursor
- CALL CUR_SET
- CLC
- RET
-
- ;----------------------------------------------------------------------
- CURSOR_LEFT PROC NEAR
-
- MOV CL,CURSOR_COL ;Is cursor
- CMP CL,7 ; at 1st column?
- JE CL_1 ;Yes, jump
-
- DEC CL ;Back up cursor
- JMP CR_1
- CL_1:
- MOV SI,PTR_ARRAY[BX] ;Start of window
-
- DEC SI ;Back one char
- CMP BYTE PTR [SI],0 ;Past start of string?
- JE CR_A ;Yes, jump
-
- MOV PTR_ARRAY[BX],SI
- CALL DISPLAY
- CLC
- RET
-
- CURSOR_LEFT ENDP
-
- CURSOR_RIGHT ENDP
-
- ;======================================================================
- ; On entry BX contains 0 = Name or 2 = String. This proc makes the
- ; selected string "active." The cursor position is determined from
- ; the pointer positions and are retained when jumping up and down.
- ; Changes: DH
- ;----------------------------------------------------------------------
- MAKE_ACTIVE PROC NEAR
-
- MOV ACTIVE,BX ;Change active index
-
- MOV CURSOR_COL,7 ;Leftmost column
-
- MOV DH,3 ;Row for name
- OR BX,BX ; if bx=0
- JZ MA_1
- MOV DH,5 ;Else row for string
- MA_1:
- MOV CURSOR_ROW,DH ;Save coords
- RET
-
- MAKE_ACTIVE ENDP
-
- ;======================================================================
- ; Load the pointers for a new macro set.
- ; Changes: BL
- ; Calls: GET_POINTER
- ;----------------------------------------------------------------------
- LOAD_POINTERS PROC NEAR
- ASSUME CS:CSEG, DS:CSEG, ES:CSEG
-
- MOV BL,MACRO_PTR ;String to look for
-
- SHL BL,1 ; they come in pairs
-
- CALL GET_POINTER ;Load pointer
- MOV NAM_PTR,SI ;Save offset
-
- INC BL ;Next string
- CALL GET_POINTER ;Load pointer
- MOV STR_PTR,SI ;Save offset
-
- RET
-
- LOAD_POINTERS ENDP
-
- ;======================================================================
- ; Find the BLth string in the list. 0-based. Returns SI pointing
- ; to the start of the string.
- ; Changes: AX,BH,SI
- ;----------------------------------------------------------------------
- GET_POINTER PROC NEAR
- ASSUME CS:CSEG, DS:CSEG, ES:CSEG
-
- MOV SI,OFFSET STRINGS ;Start scan here
- XOR BH,BH ;String counter
- GP_0:
- CMP BH,BL ;Pointing to right string?
- JNE GP_1 ;No, keep scanning
-
- RET
- GP_1:
- LODSB ;Read char
- OR AL,AL ;Is it 0?
- JNZ GP_1
- INC BH
- JMP GP_0
-
- GET_POINTER ENDP
-
- ;======================================================================
- ; This procedure will write the active string to the screen from the
- ; current cursor position forward. It is called only when a char is
- ; typed or the window is pushed.
- ; Changes: AX,BX,CX,SI
- ; Calls: CUR_SET, LOCATE_SI
- ;----------------------------------------------------------------------
- DISPLAY PROC NEAR
- ASSUME CS:CSEG, DS:CSEG, ES:CSEG
-
- CLD ;String moves forward
- CALL CUR_SET ;Position the cursor
- CALL LOCATE_SI ;Point SI to string
-
- MOV CH,CURSOR_COL
- MOV CL,COL_MAX ;Rightmost column
- DISPLAY_0:
- LODSB ;Get character
- OR AL,AL ;Is it end of string?
- JNZ DISPLAY_1 ;No, jump
-
- DEC SI ;Yes, back up
- MOV AL,20H ;Print a space
- DISPLAY_1:
- CALL CUR_SET ;Position the cursor
-
- PUSH CX ;Save register
-
- MOV AH,0AH ;Write Char
- MOV BH,VPAGE ;Active page
- MOV CX,1
- INT 10H ;Thru BIOS
-
- POP CX ;Restore register
-
- INC CURSOR_COL ;Change position
-
- CMP CL,CURSOR_COL ;Is col <= end?
- JAE DISPLAY_0 ;Yes, continue
-
- ; Past the end of the window - done with display.
-
- MOV CURSOR_COL,CH ;Return to old spot
- CALL CUR_SET ; do it
-
- RET
-
- DISPLAY ENDP
-
- ;======================================================================
- ; Get a character from the keyboard. Generate Idle interrupt for
- ; compatibility with other TSRs.
- ; Changes: AX
- ;----------------------------------------------------------------------
- GETKEY PROC NEAR
- ASSUME CS:CSEG, DS:CSEG, ES:CSEG
-
- GETKEY_1:
- INT 28H ;Generate Dos Idle
-
- MOV AH,1 ;Request KBD status
- INT 16H ; Thru BIOS
- JZ GETKEY_1 ;None ready
-
- XOR AH,AH ;Fetch the key
- INT 16H ; Thru BIOS
-
- RET
-
- GETKEY ENDP
-
- ;======================================================================
- ; The strings are stored here in ASCIIZ form.
- ;----------------------------------------------------------------------
- DB 0
- STRINGS DB 26 DUP(0,0),0 ;They start empty
- STRING_END EQU $
-
- PC = $
-
- PC = PC + 256
- STACK_TOP = PC
-
- LASTBYTE = PC
-
- CSEG ENDS
- END ENTPT
-